/*
 * @(#)GenericFactoryConnector.java   09/10/11
 * 
 * Copyright (C) 2009 Ecole des Mines d'Ales
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: yulinz88@gmail.com
 */



package fr.ema.arch3d.ref.GenericFactoryConnector;

import fr.ema.arch3d.model.architecture.configuration.ComponentClass;
import fr.ema.arch3d.model.architecture.configuration.ConnectorClass;
import fr.ema.arch3d.ref.Fractal;
import fr.ema.arch3d.ref.controllers.mixins.NoElementTypeException;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.factory.GenericFactory;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;

//~--- classes ----------------------------------------------------------------

/**
 * Class description
 *
 *
 * @version        1.0, 09/09/13
 * @author         Huaxi (Yulin) ZHANG
 */
public class GenericFactoryConnector {

    /**
     * Method description
     *
     * @param name  
     * @param type  
     * @param controllerDesc  
     * @param contentDesc  
     * @param elementType  
     * @return  
     * @throws InstantiationException  
     * @throws NoElementTypeException  
     * @throws NoSuchInterfaceException  
     */
    public Component newFcInstance(final String name, final Type type, final Object controllerDesc,
                                   final Object contentDesc, final String elementType)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component      boot      = Fractal.getBootstrapComponent();
        GenericFactory gf        = Fractal.getGenericFactory(boot);
        TypeFactory    tf        = Fractal.getTypeFactory(boot);
        Component      component = gf.newFcInstance(type, controllerDesc, contentDesc);

        if (elementType.equals("container") || elementType.equals("component") || elementType.equals("connector")) {
            Fractal.getElementtypeController(component).setElementType(elementType);
        } else {
            throw new NoElementTypeException("ElementType is not valid. "
                                             + "It must be either connector, component or container.");
        }

        Fractal.getNameController(component).setFcName(name);

        return component;
    }

    /**
     * Method description
     *
     * @param name  
     * @param its  
     * @param controllerDesc  
     * @param contentDesc  
     * @param elementType  
     * @return  
     * @throws InstantiationException  
     * @throws NoElementTypeException  
     * @throws NoSuchInterfaceException  
     */
    public Component newFcInstance(final String name, final InterfaceType[] its, final Object controllerDesc,
                                   final Object contentDesc, final String elementType)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component       boot = Fractal.getBootstrapComponent();
        GenericFactory  gf   = Fractal.getGenericFactory(boot);
        TypeFactory     tf   = Fractal.getTypeFactory(boot);
        InterfaceType[] ith  = new InterfaceType[its.length];

        for (int i = 0; i < ith.length; i++) {
            if (its[i].isFcClientItf()) {
                ith[i] = tf.createFcItfType(its[i].getFcItfName(), its[i].getFcItfSignature(), true,
                                            its[i].isFcOptionalItf(), its[i].isFcCollectionItf());
            } else {
                ith[i] = tf.createFcItfType(its[i].getFcItfName(), its[i].getFcItfSignature(), false,
                                            its[i].isFcOptionalItf(), its[i].isFcCollectionItf());
            }
        }

        ComponentType ct        = tf.createFcType(ith);
        Component     component = gf.newFcInstance(ct, controllerDesc, contentDesc);

        if (elementType.equals("container") || elementType.equals("component") || elementType.equals("connector")) {
            Fractal.getElementtypeController(component).setElementType(elementType);
        } else {
            throw new NoElementTypeException("ElementType is not valid. "
                                             + "It must be either connector, component or container.");
        }

        Fractal.getNameController(component).setFcName(name);

        // System.out.println(((Interface)component.getFcInterface("r")).isFcInternalItf());
        return component;
    }

    /**
     * This method creates the component instance according its component in configuration level.
     * @param name: instance name
     * @param c:component in configuration level
     * @return component instance which creates in julia.
     * @throws NoElementTypeException
     * @throws NoSuchInterfaceException
     * @throws InstantiationException
     */
    public Component newFcInstanceComponent(String name, ComponentClass c)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component       boot = Fractal.getBootstrapComponent();
        GenericFactory  gf   = Fractal.getGenericFactory(boot);
        TypeFactory     tf   = Fractal.getTypeFactory(boot);
        InterfaceType[] its  = new InterfaceType[c.getCmpType().getInterfaces().size()];

        for (int i = 0; i < its.length; i++) {
            its[i] = (InterfaceType) c.getCmpType().getInterfaces().get(i);
        }

        Component ci = newFcInstance(name, its, "cPrimitive", c.getImplementation(), "component");

        Fractal.getComponentDescriptorController(ci).setComponent(c);

        return ci;
    }

    /**
     * This method creates the component instance according its component in configuration level.
     * @param name: instance name
     * @param c:component in configuration level
     * @return component instance which creates in julia.
     * @throws NoElementTypeException
     * @throws NoSuchInterfaceException
     * @throws InstantiationException
     */
    public Component newFcInstanceComponentGUI(String name, ComponentClass c)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component       boot = Fractal.getBootstrapComponent();
        GenericFactory  gf   = Fractal.getGenericFactory(boot);
        TypeFactory     tf   = Fractal.getTypeFactory(boot);
        InterfaceType[] its  = new InterfaceType[c.getCmpType().getInterfaces().size()];

        for (int i = 0; i < its.length; i++) {
            its[i] = (InterfaceType) c.getCmpType().getInterfaces().get(i);
        }

        Component ci = newFcInstance(name, its, "GUIPrimitive", c.getImplementation(), "component");

        Fractal.getComponentDescriptorController(ci).setComponent(c);

        return ci;
    }

    /**
     * This method creates the connector instance according its connector in configuration level.
     * @param name: instance name
     * @param c:connector in configuration level
     * @return connector instance which creates in julia.
     * @throws NoElementTypeException
     * @throws NoSuchInterfaceException
     * @throws InstantiationException
     */
    public Component newFcInstanceConnector(String name, ConnectorClass c)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component ci = newFcInstance(name, c.getConnectorType().getInterfaces(), "connectorPrimitive", c.connectorImpl, "connector");

        Fractal.getConnectorController(ci).setConnector(c);

        return ci;
    }

    /**
     * Method description
     *
     * @param assName  
     * @param name  
     * @param config  
     * @return  
     * @throws InstantiationException  
     * @throws NoElementTypeException  
     * @throws NoSuchInterfaceException  
     */
    public Component newFcInstanceAssembly(String assName, String name,
            fr.ema.arch3d.model.architecture.configuration.Configuration config)
            throws InstantiationException, NoSuchInterfaceException, NoElementTypeException {
        Component      boot     = Fractal.getBootstrapComponent();
        GenericFactory gf       = Fractal.getGenericFactory(boot);
        TypeFactory    tf       = Fractal.getTypeFactory(boot);
        ComponentType  archType = tf.createFcType(new InterfaceType[] {});
        Component      a        = newFcInstance(name, archType, "architecture", null, "container");

        Fractal.getAssemblyDescriptorController(a).getCurrentAssDescription();
        Fractal.getAssemblyDescriptorController(a).setConfiguration(config);
        Fractal.getAssemblyDescriptorController(a).setAssName(assName);

        return a;
    }
}
